
local assert     = assert
local require    = require    
local bit        = bit
local coroutine  = coroutine
local debug      = debug
local io         = io
local pairs      = pairs
local ipairs     = ipairs
local math       = math
local os         = os
local print      = print
local pcall      = pcall
local xpcall     = xpcall
local rawget     = rawget
local rawset     = rawset
local select     = select
local string     = string
local table      = table
local tonumber   = tonumber
local tostring	 = tostring
local error      = error
local type       = type
local unpack     = unpack
local setmetatable = setmetatable
local getmetatable = getmetatable
local ngx = ngx
local ngx_now = ngx.now
local math_floor = math.floor

local table_concat = table.concat
local table_loadstring = table.loadstring


----数据变量----
local log     = require("modules.commhm.log");
local ns_http = require("modules.commhm.resty.http");
local uu      = require("modules.commhm.utils")


local FOO = {
	_VERSION = '0.12.1',

	__POOL_SIZE__ = 1024,           ---连接池大小
	__POOL_ALIVE_TIME__ = 65500,    ---连接在连接池中保持存活时间
};

-- 请求内部网页 ( timer里面没有ngx.ctx 无法调用 )
function   FOO.getHttpPageInner( url_ )
	local now_ = ngx_now()
	local resp, err =  ngx.location.capture( url_ );     --同机器内请求
	local cost_ = math_floor( (ngx_now() - now_) * 1000 )
	if  not resp then
		log.error( "getHttpPageInner|" .. (err or "nil") .. "|" .. cost_ .. "|" .. url_ );
	end
	local code_ = resp.status or 505;
	local body_ = resp.body or "";
	return code_, body_, resp;
end


-- 请求外部page 长连接
function  FOO.getHttpPage( url_, headers_, ssl_, p_time_exp_ )
	log.debug( log.fast() and "call getHttpPage=" .. url_ );
	local httpc     = ns_http.new()

	local time_exp_ = p_time_exp_ or 9000
	httpc:set_timeouts(time_exp_, time_exp_, time_exp_);

	local hds_      = headers_ or { ["Connection"]="Keep-Alive",
									["User-Agent"]="mnw",
									["X-Forwarded-For"]=(ngx.ctx.xff or ngx.var.remote_addr) };

	local params_   = { method="GET", headers=hds_,
						keepalive_timeout = FOO.__POOL_ALIVE_TIME__,
						keepalive_pool = FOO.__POOL_SIZE__,
						ssl_verify = ssl_ or false,}

	local now_ = ngx_now()
	local resp, err = httpc:request_uri( url_, params_ );
	local cost_ = math_floor( (ngx_now() - now_) * 1000 )
	if  not resp then
		httpc:close()

		if  err == "closed" or err == "broken pipe" or err == "connection reset by peer" then
			--对方已经关闭 重连
		else
			-- log.error( "getHttpPage_1|" .. (err or "nil") .. "|\n" .. cost_ .. "|" .. url_ );
			log.day_list( "httpfail", "getHttpPage_1|" .. (err or "nil") .. "|\n" .. cost_ .. "|" .. url_ );            
		end

		local httpc2 = ns_http.new()
		httpc2:set_timeouts(time_exp_, time_exp_, time_exp_);

		now_ = ngx_now()
		resp, err = httpc2:request_uri( url_, params_ );
		cost_ = math_floor( (ngx_now() - now_) * 1000 )

		if  not resp then
			httpc2:close()
			log.error( "getHttpPage_retry|" .. (err or "nil") .. "|" .. cost_ .. "|" .. url_ );
			--log.day( "error", "getHttpPage_retry|" .. (err or "nil") .. "|" .. url_ );
			return 505;
		end
	end

	--log.debug( "body=" .. resp.body );
	local code_ = resp.status or 505;
	local body_ = resp.body or "";
	return code_, body_, resp;
end


-- post外部page
function  FOO.postHttpPage( url_, body_, p_time_exp_, headers_ )
	local httpc = ns_http.new()

	local time_exp_ = p_time_exp_ or 9000
	httpc:set_timeouts(time_exp_, time_exp_, time_exp_);

	local params_ = {
		method = "POST",
		body = body_,
		headers = { ["Connection"]="Keep-Alive",
					["User-Agent"]="mnw",
					["Content-Type"] = "application/x-www-form-urlencoded",
					["X-Forwarded-For"]=(ngx.ctx.xff or ngx.var.remote_addr), },
		keepalive_timeout = FOO.__POOL_ALIVE_TIME__,
		keepalive_pool = FOO.__POOL_SIZE__,

		--get_reused_times_func = function(httpc_obj)
			--local times_, err_ = httpc_obj:get_reused_times()
			--log.debug_list( "get_reused_times:", times_, err_, httpc_obj.sock )
			--uu.var_dump( httpc_obj.sock )
		--end,
	}

	if  type(headers_) == 'table' then
		for k, v in pairs(headers_) do
			--log.debug( "set headers:" .. k  .. "=" .. v )
			params_.headers[k] = v
		end
	end


	local now_ = ngx_now()
	local resp, err = httpc:request_uri( url_,	params_ );
	local cost_ = math_floor( (ngx_now() - now_) * 1000 )
	if  not resp then
		httpc:close()

		if  err == "closed" or err == "broken pipe" or err == "connection reset by peer" then
			--对方已经关闭 重连
		else
			log.debug( "postHttpPage_1|" .. (err or "nil") .. "|" .. cost_ .. "|\n" .. url_ );
		end

		local httpc2 = ns_http.new()
		httpc2:set_timeouts(time_exp_, time_exp_, time_exp_);

		now_ = ngx_now()
		resp, err = httpc2:request_uri( url_, params_ );
		cost_ = math_floor( (ngx_now() - now_) * 1000 )
		if  not resp then
			httpc2:close()
			local error_msg_ = { 'postHttpPage_retry', (err or "nil"), cost_, url_ }
			if  body_ and #body_ < 512 then
				error_msg_[ #error_msg_ + 1 ] = body_
			else
				error_msg_[ #error_msg_ + 1 ] = #body_
			end
			log.error( table_concat( error_msg_, '|' ) )
			return 505;
		end

	end

	local code_ = resp.status or 505;
	local body_ = resp.body or "";
	return code_, body_, resp;
end


-- post外部page https
function  FOO.postHttpsPage( url_, body_, ssl_, p_time_exp_, headers_ )
	local httpc = ns_http.new()

	local time_exp_ = p_time_exp_ or 9000
	httpc:set_timeouts(time_exp_, time_exp_, time_exp_);

	local params_ = {
		ssl_verify = ssl_ or false,
		method = "POST",
		body = body_,

		headers = { ["Connection"]="Keep-Alive",
					["User-Agent"]="mnw",
					["Content-Type"] = "application/x-www-form-urlencoded",
					["X-Forwarded-For"]=(ngx.ctx.xff or ngx.var.remote_addr), },
		keepalive_timeout = FOO.__POOL_ALIVE_TIME__,
		keepalive_pool = FOO.__POOL_SIZE__,
	}

	if  type(headers_) == 'table' then
		for k, v in pairs(headers_) do
			--log.debug( "set headers:" .. k  .. "=" .. v )
			params_.headers[k] = v
		end
	end


	local now_ = ngx_now()
	local resp, err = httpc:request_uri( url_, params_	);
	local cost_ = math_floor((ngx_now() - now_) * 1000 )
	if  not resp then
		httpc:close()

		if  err == "closed" then
			--对方已经关闭 重连
		else
			log.error( "postHttpsPage_1|" .. (err or "nil") .. "|" .. cost_ .. "|\n" .. url_ );
		end

		--出错重试
		local httpc2 = ns_http.new()
		httpc2:set_timeouts(time_exp_, time_exp_, time_exp_);

		now_ = ngx_now()
		resp, err = httpc2:request_uri( url_, params_ );
		cost_ = math_floor( (ngx_now() - now_) * 1000 )
		if  not resp then
			--重试也失败
			httpc2:close()
			local error_msg_ = { 'postHttpsPage_retry', (err or "nil"), cost_, url_ }
			if  body_ and #body_ < 512 then
				error_msg_[ #error_msg_ + 1 ] = body_
			else
				error_msg_[ #error_msg_ + 1 ] = #body_
			end
			log.error( table_concat( error_msg_, '|' ) )

			return 505;
		end

	end

	local code_ = resp.status or 505;
	local body_ = resp.body or "";
	return code_, body_, resp;
end


-- put外部page (华为云obs)
function  FOO.send_HttpPage( url_, method_, body_, p_time_exp_, headers_ )
	local httpc = ns_http.new()

	local time_exp_ = p_time_exp_ or 9000
	httpc:set_timeouts(time_exp_, time_exp_, time_exp_);

	local params_ = {
		method = method_,
		body = body_,
		headers = {},
	}

	if  headers_ then
		for k, v in pairs(headers_) do
			--log.debug( "set headers:" .. k  .. "=" .. v )
			params_.headers[k] = v
		end
	end


	local now_ = ngx_now()
	local resp, err = httpc:request_uri( url_,	params_ );
	local cost_ = math_floor( (ngx_now() - now_) * 1000 )
	if  not resp then
		httpc:close()

		if  err == "closed" or err == "broken pipe" or err == "connection reset by peer" then
			--对方已经关闭 重连
		else
			log.error( "postHttpPage_1|" .. (err or "nil") .. "|" .. cost_ .. "|\n" .. url_ );
		end

		local httpc2 = ns_http.new()
		httpc2:set_timeouts(time_exp_, time_exp_, time_exp_);

		now_ = ngx_now()
		resp, err = httpc2:request_uri( url_, params_ );
		cost_ = math_floor( (ngx_now() - now_) * 1000 )
		if  not resp then
			httpc2:close()
			log.error( "postHttpPage_retry|" .. (err or "nil") .. "|" .. cost_ .. "|" .. url_ );
			--log.day( "error", "postHttpPage_retry|" .. (err or "nil") .. "|" .. url_ );
			return 505;
		end

	end

	local code_ = resp.status or 505;
	local body_ = resp.body or "";
	return code_, body_, resp;
end



--proxy_pass http 长连接穿透
function FOO.proxyHttp( host_, port_ )
	local httpc = ns_http.new()

	--local time_exp_ = p_time_exp_ or 9000
	--httpc:set_timeouts(time_exp_, time_exp_, time_exp_);

	local ok, err = httpc:connect(host_, port_)
	if  not ok then
		ngx.log(ngx.ERR, "error_proxyHttp_connect:" .. uu.to_str(err) )
		return 0
	end

	local times = httpc:get_reused_times()
	--if  err then
		--ngx.log(ngx.ERR, "error_proxyHttp_get_reused_times:" .. uu.to_str(err) )
		--httpc:close()
		--return 0
	--end

	httpc:proxy_response(httpc:proxy_request())
	httpc:set_keepalive( FOO.__POOL_ALIVE_TIME__, FOO.__POOL_SIZE__ * 5 )  --网关的pool_size加大

	--log.debug( "proxyHttp get_reused_times=" .. (times or "nil") .. ", err=" .. (err or "nil") );
	return times or 0;
end



-- 上面 proxyHttp 的变种，不返回给客户端，直接返回给中间调用者,一般用来显示日志之类
-- 返回一个table t,  t.ret=0成功   t.ret~=0失败
function FOO.getProxyHttpString( host_, port_ )
	local httpc = ns_http.new()

	--local time_exp_ = p_time_exp_ or 9000
	--httpc:set_timeouts(time_exp_, time_exp_, time_exp_);

	local ok, err = httpc:connect(host_, port_)
	if not ok then
		ngx.log(ngx.ERR, "error_proxyHttp:" .. uu.to_str(err) )
		--return 0, 'ERROR_CONNECT'
		return { ret=1, err='ERROR_CONNECT' }   --失败
	end

	local times = httpc:get_reused_times()
	--if  err then
		--ngx.log(ngx.ERR, "error_proxyHttp_get_reused_times:" .. uu.to_str(err) )
		--httpc:close()
		--return 0
	--end

	local response, chunksize = httpc:proxy_request()
	local ret_string_ = FOO.proxy_response_buffer( response, chunksize )

	httpc:set_keepalive( FOO.__POOL_ALIVE_TIME__, FOO.__POOL_SIZE__ * 5 )  --网关的pool_size加大
	--log.debug( "proxyHttp get_reused_times=" .. (times or "nil") .. ", err=" .. (err or "nil") );
	--return times, response, ret_string_
	local fret_ = {
		ret=0,
		response = response,
		chunksize = chunksize,
		ret_string = ret_string_,
		rts = times,               --reused_times
	}
	return fret_
end



-- resty/http proxy_response 的变种，截留字符串存到buffer里
function FOO.proxy_response_buffer(response, chunksize)
	if  not response then
		log.error("no response provided")
		return
	end

	-- 不用处理 ngx_header
	--ngx.status = response.status
	-- Filter out hop-by-hop headeres
	--for k, v in pairs(response.headers) do
		--if not HOP_BY_HOP_HEADERS[str_lower(k)] then
			--ngx_header[k] = v
		--end
	--end

	local ret_thunks_ = {}
	local reader = response.body_reader
	repeat
		local chunk, err = reader(chunksize)
		if  err then
			log.error(err)
			break
		end
		if  chunk then
			--log.debug( 'chunk=' .. uu.to_str(chunk) )
			ret_thunks_[ #ret_thunks_ + 1 ] = chunk
		end
	until not chunk
	return table_concat(ret_thunks_)
end


-- 抓取http 返回值是 txt
function  FOO.http_get_txt( url, headers_, ssl, exp_time_ )
	if  url then
		local ret, body = FOO.getHttpPage( url, headers_, ssl, exp_time_ )
		if  log.fast() then
			log.debug( 'http code=' .. uu.to_str(ret) )
		end
		if  body then
			return body
		else
			log.error( 'ret_not_string:' .. uu.to_str(body) )
		end
	end
	return nil
end



-- 抓取http, 返回值是一个 lua-table， 否则返回nil
function  FOO.http_get_lua( url, headers_, ssl, exp_time_ )
	if  url then
		local ret, body = FOO.getHttpPage( url, headers_, ssl, exp_time_ )
		if  log.fast() then
			log.debug( 'http code=' .. uu.to_str(ret) )
		end
		if  body then
			return table_loadstring( body )
		else
			log.error( 'ret_not_lua_table:' .. uu.to_str(body) )
		end
	end
	return nil
end


-------------------------------------------------------------------
---鉴黄鉴爆鉴政(新接口)
---返回值1 网络失败返回500 成功返回0 失败返回>0
---返回值2 Confidence= [0到100] 确认程度
FOO.send_data_to_jh_new = function( infos_ )
	log.debug( log.fast() and "call send_data_to_jh_new, url=" .. infos_.url )
	local url_ = 'https://operation.mini1.cn/api/newAuthImage';

	local post_data_ = { type=(infos_.type or '111'), imageUrl=infos_.url };
	local json_ = uu.table2json( post_data_ )
	--log.debug( json_ );

	local ret_, body_ = FOO.postHttpsPage( url_, json_, nil, 30000 );
	log.debug( log.fast() and "jh ret= " .. (ret_ or 'nil') .. ", body=" .. (body_ or 'nil') )


	local fret_, op_
	local Confidence_ = 0
	if  ret_==200 then
		local ret_table_ = uu.json2table( body_ );
		--{"code":2000,"message":"success","data":{"result":0,"type":0,"Confidence":73}}
		--{"code":2000,"message":"success","data":{"result":2,"type":1,"Confidence":90}}
		--{"code":"FailedOperation.DownLoadError","message":"\u4e0b\u8f7d\u5931\u8d25","data":[]}
		if  type(ret_table_)=='table' then
			if  ret_table_.code == 2000 and ret_table_.data and ret_table_.data.result then
				if  ret_table_.data.result == 0 then
					fret_, op_   = 0, "ok"   --明确成功
				else
					fret_, op_   = 1, "fail" --明确失败
					if  ret_table_.data.Confidence then
						Confidence_ = ret_table_.data.Confidence
					end
				end
			end
		end
	end

	if  not fret_ then
		op_   = "error_jh"    --各种错误，需要重试
		fret_ = 500
		log.day_list( "error", op_,  infos_.uin, infos_.reason, infos_.url, ret_, body_, fret_, Confidence_ )
	end

	log.day_list( "jh",     op_,  infos_.uin, infos_.reason, infos_.url, ret_, body_, fret_, Confidence_ )
	return fret_, Confidence_
end



---鉴黄鉴爆鉴政(批量新接口)
---返回值1 网络失败返回500 成功返回0 失败返回>0
---返回值2 Confidence= [0到100] 确认程度
FOO.send_data_to_jh_new_batch = function( infos_ )
	log.debug( log.fast() and "call send_data_to_jh_new_batch" )
	local url_ = 'https://operation.mini1.cn/api/newAuthImage';

	local post_data_ = { type=(infos_.type or '111'), imageUrl=infos_.url };
	local json_ = uu.table2json( post_data_ )
	--log.debug( json_ );

	local ret_, body_ = FOO.postHttpsPage( url_, json_, nil, 30000 );
	log.debug( log.fast() and "jh ret= " .. (ret_ or 'nil') .. ", body=" .. (body_ or 'nil') )


	local fret_, op_
	local Confidence_ = 0
	if  ret_==200 then
		local ret_table_ = uu.json2table( body_ );
		--{"code":2000,"message":"success","data":{"result":0,"type":0,"Confidence":73}}
		--{"code":2000,"message":"success","data":{"result":2,"type":1,"Confidence":90}}
		--{"code":"FailedOperation.DownLoadError","message":"\u4e0b\u8f7d\u5931\u8d25","data":[]}
		if  type(ret_table_)=='table' then
			if  ret_table_.code == 2000 and ret_table_.data and ret_table_.data.result then
				if  ret_table_.data.result == 0 then
					fret_, op_   = 0, "ok"   --明确成功
				else
					fret_, op_   = 1, "fail" --明确失败
					if  ret_table_.data.Confidence then
						Confidence_ = ret_table_.data.Confidence
					end
				end
			end
		end
	end

	if  not fret_ then
		op_   = "error_jh"    --各种错误，需要重试
		fret_ = 500
		log.day_list( "error", op_,  infos_.uin, infos_.reason, infos_.url, ret_, body_, fret_, Confidence_ )
	end

	log.day_list( "jh",     op_,  infos_.uin, infos_.reason, infos_.url, ret_, body_, fret_, Confidence_ )
	return fret_, Confidence_
end






--远程日志地址
FOO.log_net_url = nil

--profile分离后，用来打印远程日志
FOO.log_net_remote = function( fn_, txt_ )
	local post_data1_ = { fn=fn_, c=txt_, n=(ngx.ctx.m_params.act or ngx.ctx.m_params.cmd),
						  xff=(ngx.ctx.xff or ngx.var.remote_addr) };
	local post_data2_ = ngx.encode_args(post_data1_);
	--log.debug( post_data2_ );
	local ret, body = FOO.postHttpPage( FOO.log_net_url, post_data2_ );
	if  ret == 200 and body then
		-- ok
	else
		log.error( "log_net_remote error:" ..  post_data2_ )
	end

	--log.debug( "============profile_log_net===========" );
	--log.debug( ret );
	--log.debug( body );
end



--{ 
	--method = "GET", --"POST"
	--path = "/search?q=hello",
	--method = "POST",
	--body = "a=1&b=2",
	--headers = {
		--["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36"
		--["Content-Type"] = "application/x-www-form-urlencoded",
	--}
--}

-----------------------------------url code------------
-- ngx.escape_uri	字符串的url编码
-- ngx.unescape_uri	字符串url解码
-- ngx.encode_args	将table编码为一个参数字符串
-- ngx.decode_args	将参数字符串编码为一个table
-- ngx.encode_base64	字符串的base64编码
-- ngx.decode_base64	字符串的base64解码
-- ngx.crc32_short	字符串的crs32_short哈希
-- ngx.crc32_long	字符串的crs32_long哈希
-- ngx.hmac_sha1	字符串的hmac_sha1哈希
-- ngx.md5	返回16进制MD5
-- ngx.md5_bin	返回2进制MD5
-- ngx.sha1_bin	返回2进制sha1哈希值
-- ngx.quote_sql_str	SQL语句转义


return FOO;
